import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;

/**
 * Clasa Logic reprezinta un nod logic responsabil sa ofere metode de acces de
 * nivel inalt la date, utilizand mecanismul RMI.
 * 
 * Nodul Logic ofera clientilor acces la functionalitatile nodului datelor -
 * disponibil prin mecanism RMI de asemenea.
 * 
 * Nodul Logic jurnalizeaza actiunile clientilor prin nodul jurnalizator - nod
 * de asemenea disponibil prin mecanism RMI.
 */
public class Logic extends UnicastRemoteObject implements RILogic {

	/**
	 * Default serial version.
	 */
	private static final long serialVersionUID = 1L;

	private static final String LOGIC_NAME = "Logic";
	private static final String DATA_NAME = "Data";
	private static final String LOGGER_NAME = "Logger";

	/**
	 * Referinta "remote" la nodul de date.
	 */
	protected RIData rmiDataNode;

	/**
	 * Referinta "remote" la nodul jurnalizator.
	 */
	protected RILogger rmiLoggerNode;

	/**
	 * Construieste un nod logic.
	 * 
	 * La momentul crearii se obtine o referinta la nodul datelor si la nodul
	 * jurnalizator
	 * 
	 * @param sDataName
	 *            numele nodului datelor
	 * @param sDataNodeMachine
	 *            IP/masina nodului datelor
	 * @param sLoggerName
	 *            numele nodului jurnalizator
	 * @param sLoggerNodeMachine
	 *            IP/masina nodului jurnalizator
	 */
	public Logic(String sDataName, String sDataNodeMachine, String sLoggerName, String sLoggerNodeMachine) throws RemoteException, NotBoundException,
			MalformedURLException {

		// Obtinerea referintei "remote" la nodul datelor care se poate afla pe
		// o alta masina.
		this.rmiDataNode = (RIData) Naming.lookup("rmi://" + sDataNodeMachine + "/" + sDataName);

		// Obtinerea referintei "remote" la nodul jurnaliztor care se poate afla
		// pe o alta masina.
		this.rmiLoggerNode = (RILogger) Naming.lookup("rmi://" + sLoggerNodeMachine + "/" + sLoggerName);
	}

	public synchronized String getAllStudents(String idClient) throws RemoteException {

		// Logarea actiunii folosit nodul Logger obtinut prin mecanism RMI.
		rmiLoggerNode.log(idClient, Logger.L_STUD);

		ArrayList vStudent = this.rmiDataNode.getAllStudentRecords();

		String sReturn = "";
		for (int i = 0; i < vStudent.size(); i++) {
			sReturn += (i == 0 ? "" : "\n") + ((Student) vStudent.get(i)).toString();
		}
		return sReturn;
	}

	public synchronized String getAllCourses(String idClient) throws RemoteException {

		// Logarea actiunii folosit nodul Logger obtinut prin mecanism RMI.
		rmiLoggerNode.log(idClient, Logger.L_CURS);

		ArrayList vCourse = this.rmiDataNode.getAllCourseRecords();

		String sReturn = "";
		for (int i = 0; i < vCourse.size(); i++) {
			sReturn += (i == 0 ? "" : "\n") + ((Course) vCourse.get(i)).toString();
		}
		return sReturn;
	}

	public synchronized String getRegisteredStudents(String idClient, String sCID) throws RemoteException {

		// Logarea actiunii folosit nodul Logger obtinut prin mecanism RMI.
		rmiLoggerNode.log(idClient, Logger.L_STUD_INREG_UN_CURS);

		Course objCourse = this.rmiDataNode.getCourseRecord(sCID);
		if (objCourse == null) {
			return "ID curs inexistent";
		}
		ArrayList vStudent = objCourse.getRegisteredStudents();

		String sReturn = "";
		for (int i = 0; i < vStudent.size(); i++) {
			sReturn += (i == 0 ? "" : "\n") + ((Student) vStudent.get(i)).toString();
		}
		return sReturn;
	}

	public synchronized String getRegisteredCourses(String idClient, String sSID) throws RemoteException {

		// Logarea actiunii folosit nodul Logger obtinut prin mecanism RMI.
		rmiLoggerNode.log(idClient, Logger.L_CURS_INREG_UN_STUD);

		Student objStudent = this.rmiDataNode.getStudentRecord(sSID);
		if (objStudent == null) {
			return "ID student inexistent";
		}
		ArrayList vCourse = objStudent.getRegisteredCourses();

		String sReturn = "";
		for (int i = 0; i < vCourse.size(); i++) {
			sReturn += (i == 0 ? "" : "\n") + ((Course) vCourse.get(i)).toString();
		}
		return sReturn;
	}

	public synchronized String getCompletedCourses(String idClient, String sSID) throws RemoteException {

		// Logarea actiunii folosit nodul Logger obtinut prin mecanism RMI.
		rmiLoggerNode.log(idClient, Logger.L_CURS_ABS);

		Student objStudent = this.rmiDataNode.getStudentRecord(sSID);
		if (objStudent == null) {
			return "ID student inexistent";
		}
		ArrayList vCourseID = objStudent.getCompletedCourses();

		String sReturn = "";
		for (int i = 0; i < vCourseID.size(); i++) {
			String sCID = (String) vCourseID.get(i);
			String sName = this.rmiDataNode.getCourseName(sCID);
			sReturn += (i == 0 ? "" : "\n") + sCID + " " + (sName == null ? "Unknown" : sName);
		}
		return sReturn;
	}

	public synchronized String makeARegistration(String idClient, String sSID, String sCID) throws RemoteException {

		// Logarea actiunii folosit nodul Logger obtinut prin mecanism RMI.
		rmiLoggerNode.log(idClient, Logger.INREG_STUD_CURS);

		Student objStudent = this.rmiDataNode.getStudentRecord(sSID);
		Course objCourse = this.rmiDataNode.getCourseRecord(sCID);
		if (objStudent == null) {
			return "ID student inexistent";
		}
		if (objCourse == null) {
			return "ID curs inexistent";
		}

		ArrayList vCourse = objStudent.getRegisteredCourses();
		for (int i = 0; i < vCourse.size(); i++) {
			if (((Course) vCourse.get(i)).conflicts(objCourse)) {
				return "Conflicte de inregistrare la curs";
			}
		}

		this.rmiDataNode.makeARegistration(sSID, sCID);
		return "Succes!";
	}

	public synchronized String clientExit(String idClient) throws RemoteException {

		// Logarea actiunii folosit nodul Logger obtinut prin mecanism RMI.
		this.rmiLoggerNode.log(idClient, Logger.IESIRE);

		return null;
	}

	public static void main(String args[]) {

		// Verificarea numarului de parametri.
		if (args.length != 2) {
			System.out.println("Numar incorect de parametrii");
			System.out.println("Utilizare: java Logic <IP/masina unde ruleaza nodul Data> <IP/masina unde ruleaza nodul Logger>");
			System.exit(1);
		}

		try {
			
			// Crearea unui nod logic si publicarea prin RMI
			Logic objLogic = new Logic(DATA_NAME, args[0], LOGGER_NAME, args[1]);
			Naming.rebind(LOGIC_NAME, objLogic);
			System.out.println("Nodul logic este gata de servire.");

			System.out.println("Apasati Enter pentru terminare.");
			System.in.read();

			Naming.unbind(LOGIC_NAME);
			System.out.println("Nodul logic se dezactiveaza. Apasati Ctrl-C daca dureaza prea mult.");
		} catch (java.rmi.ConnectException e) {
			System.err.println("Java RMI error: verificati daca rmiregistry este pornit.");
			System.exit(1);
		} catch (java.rmi.NotBoundException e) {
			System.err.println("Java RMI error: verificati daca nodul datelor este pornit.");
			System.exit(1);
		} catch (Exception e) {
			System.out.println("Unexpected exception at " + LOGIC_NAME);
			e.printStackTrace();
			System.exit(1);
		}
	}

}